package com.smartcommunity.faceengine.impl;

import com.github.rholder.retry.*;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.smartcommunity.bean.model.Result;
import com.smartcommunity.entity.facelib.sensenets2.SenseNets2FaceDetect;
import com.smartcommunity.entity.facelib.sensenets2.SenseNets2FaceResponse;
import com.smartcommunity.entity.facelib.sensenets2.SenseNets2FaceResponseResult;
import com.smartcommunity.entity.facelib.sensenets2.SenseNets2FaceResponseResultItem;
import com.smartcommunity.faceengine.FaceEngineService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.StopWatch;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

@Service("sensenets2")
@Slf4j
public class SenseNets2FaceEngineServiceImpl implements FaceEngineService {

    @Value("${engine.type}")
    String engineType;

    @Value("${engine.url}")
    String engineUrl;

    @Override
    public Result<SenseNets2FaceResponse> detectFaceFeature(SenseNets2FaceDetect param) {
        if (param.getRetry() > 0) {
            return retryMethod(param);
        }
        return requestEngine(param);
    }

    @Override
    public Result<SenseNets2FaceResponse> detectFaceFeature(String imgUri) {
        return this.requestEngine(SenseNets2FaceDetect.createInstance(Lists.newArrayList(imgUri),0));
    }


    @Override
    public Result<Map<String,String>> detectFaceFeature(List<String> urls) {
        // 每16张图片一组，批量调用接口提取特征
        final List<List<String>> batch16Features = splitListWithLimitSize(urls);

        final Result mapResult = Result.ok();
        final Map<String,String> resultMap = new ConcurrentHashMap(urls.size());
        batch16Features.parallelStream().filter(CollectionUtils::isNotEmpty)
                .forEach(imgUrl16List -> {

                    Result<SenseNets2FaceResponse> result =
                            detectFaceFeature(SenseNets2FaceDetect.createInstance(imgUrl16List, 3));// 重试3次

                    if (result.getResultCode() == 200) {
                        SenseNets2FaceResponse res = result.getData();
                        if (res.getContext().getErr_code() == 0) {
                            List<SenseNets2FaceResponseResult> imgFeatList = res.getResult();
                            if(CollectionUtils.isNotEmpty(imgFeatList)){
                                for (int i = 0; i < imgFeatList.size(); i++) {
                                    SenseNets2FaceResponseResult imgResult = imgFeatList.get(i);
                                    String currImgUrl = imgUrl16List.get(i);
                                    if(Objects.nonNull(imgResult.getErr())) {
                                        log.error("图片提取特征失败。图片：{}，失败提示：{}", currImgUrl,imgResult.getErr());
                                        continue;
                                    }
                                    Optional.of(imgResult).map(SenseNets2FaceResponseResult::getItems)
                                            .map(l->l.get(0))
                                            .map(SenseNets2FaceResponseResultItem::takeFeature)
                                            .ifPresent(first->{
                                                resultMap.put(currImgUrl,first);
                                            });
                                }}
                        }
                    } else {
                        log.error("批量图片提取特征失败。参数:{}，响应:{}", imgUrl16List,result);
                        mapResult.setResultCode( result.getResultCode());
                        mapResult.setResultMessage( result.getResultMessage());
                    }
                });
        if(mapResult.isOk()){
            mapResult.setData(resultMap);
        }

        return mapResult;
    }

    private static List<List<String>> splitListWithLimitSize(List<String> urls) {
        final List<List<String>> list16 = Lists.newArrayList();
        if(CollectionUtils.isEmpty(urls)){
            return list16;
        }
        List<String> first = Lists.newArrayListWithExpectedSize(16);
        list16.add(first);
        urls.forEach(f -> {
            if (list16.get(list16.size()-1).size() == 16) {
                list16.add(Lists.newArrayListWithExpectedSize(16));
            }
            list16.get(list16.size()-1).add(f);
        });
        return list16;
    }

    public Result<SenseNets2FaceResponse> requestEngine(SenseNets2FaceDetect param) {
        //TODO 测试使用，写死
        if (true){
            return Result.ok(SenseNets2FaceResponseResult.getDemoFaceResponse());
        }
        log.info("调用深网2.0引擎提取特征开始，参数: {}", param);
        StopWatch watch = new StopWatch();
        watch.start(String.valueOf(param.hashCode()));

        RestTemplate http = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<String> httpEntity = new HttpEntity(param, headers);
        ResponseEntity<SenseNets2FaceResponse> resp = http.exchange(engineUrl, HttpMethod.POST, httpEntity, SenseNets2FaceResponse.class);

        log.debug("调用深网2.0引擎提取特征结束，结果: {}", resp);

        if (resp.getStatusCode() == HttpStatus.OK) {
            watch.stop();
            log.info("调用深网2.0引擎提取特征结束，计时 taskName:{},millis:{}", watch.currentTaskName(),watch.getTotalTimeMillis());
            SenseNets2FaceResponse body = resp.getBody();
            if(log.isDebugEnabled() && body.getContext().getErr_code()!=0){
                log.debug("调用深网2.0引擎提取特征分析日志。Resp:{}",body);
            }
            return Result.ok(body);
        } else {
            return Result.error(resp.getStatusCodeValue(), "引擎提取特征错误", resp.getBody());
        }
    }

    public Result<SenseNets2FaceResponse> retryMethod(SenseNets2FaceDetect param) {
        //定义重试机制
        Retryer<Result> retryer = RetryerBuilder.<Result>newBuilder()
                //retryIf 重试条件
                .retryIfException()
                .retryIfRuntimeException()
                .retryIfExceptionOfType(Exception.class)
                .retryIfException(Predicates.equalTo(new Exception()))
                .retryIfResult(r -> r.getResultCode() != 200)

                //等待策略：每次请求间隔1s
                .withWaitStrategy(WaitStrategies.fixedWait(3, TimeUnit.SECONDS))

                //停止策略 : 尝试请求6次
                .withStopStrategy(StopStrategies.stopAfterAttempt(param.getRetry()))

                //时间限制 : 某次请求不得超过2s , 类似: TimeLimiter timeLimiter = new SimpleTimeLimiter();
//                .withAttemptTimeLimiter(AttemptTimeLimiters.fixedTimeLimit(2, TimeUnit.SECONDS))

                .build();
        try {
            return retryer.call(()->requestEngine(param));
        } catch (ExecutionException e) {
            log.error("调用深网2.0引擎提取特征，重试过程中出现异常ExecutionException: {}", e.getMessage());
        } catch (RetryException e) {
            log.error("调用深网2.0引擎提取特征，重试过程中出现异常RetryException: {}", e.getMessage());
        }
        return null;
    }

    public static void main(String[] args) {
//        String param = ("{\n" +
//                "    \"imgs\": [\n" +
//                "        {\n" +
//                "            \"data\": {\n" +
//                "                \"type\": \"uri\",\n" +
//                "                \"value\": \"http://172.16.90.213:6551/DownLoadFile?filename=PFSB:/bimg/bimg/data/20200225/22_0/f85a4289a8ee79d738966e8499662f73\"\n" +
//                "            },\n" +
//                "            \"rect\": {\n" +
//                "                \"left\": 0,\n" +
//                "                \"top\": 0,\n" +
//                "                \"right\": 0,\n" +
//                "                \"bottom\": 0\n" +
//                "            },\n" +
//                "            \"params\": {\n" +
//                "                \"attribute\": \"1\",\n" +
//                "                \"feature\": \"1\"\n" +
//                "            }\n" +
//                "        }\n" +
//                "    ]\n" +
//                "}");
//
//        SenseNets2FaceEngineServiceImpl service = new SenseNets2FaceEngineServiceImpl();
//        service.engineType = "sensenets2";
//        service.engineUrl = "http://172.16.90.185:9001/engine/face/detectjson";
//        SenseNets2FaceDetect param1 = JSONObject.parseObject(param, SenseNets2FaceDetect.class);
//        param1.setRetry(4);
//        Result<SenseNets2FaceResponse> res2 = service.retryMethod(param1);
//        System.out.println(res2.toString());
        ArrayList<String> list = Lists.newArrayList();
//        IntStream.rangeClosed(1,34).forEach(i->list.add(String.valueOf(i)));
        List<List<String>> lists = splitListWithLimitSize(list);
        System.out.println(lists);
    }
}
